package edu.winona.csclub.testing.core;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;

import org.junit.Assert;

import edu.winona.csclub.testing.generators.*;
import edu.winona.csclub.testing.utils.StringWrapper;
/**
 * oh god its ugly but it works...
 */
public class TestHarness {
	private TestSuite suite;
	private int timeout;
	public static Object pinger = new Object();
	
	public TestHarness(){}
	
	/**
	 * @param suite
	 * @param timeout
	 */
	public TestHarness(TestSuite suite, int timeout) {
		super();
		this.suite = suite;
		this.timeout = timeout;
	}

	/**
	 * @return the suite
	 */
	public TestSuite getSuite() {
		return suite;
	}

	/**
	 * @param suite the suite to set
	 */
	public void setSuite(TestSuite suite) {
		this.suite = suite;
	}
	
	public List<Result> execute(){
		List<Result> res = new LinkedList<Result>();
		for( TestCase ts : suite.getCases() ){
			for( Test test : ts ){
				res.add( execute(test,timeout));
			}
		}
		return res;
	}
	
	public static synchronized Result execute(Test t, long timeout){
		Result r = new Result();
		Runtime runtime = Runtime.getRuntime();
		String res = "";
		try {
			r.setTest(t);
			Process p = runtime.exec(t.toString());
			StringWrapper s = new StringWrapper();
			s.string=res;
			OutCounter c = new OutCounter();
			OutListener o = new OutListener(new BufferedInputStream(p.getErrorStream()),s,c);
			OutListener o2 = new OutListener(new BufferedInputStream(p.getInputStream()),s,c);
			Thread thread = new Thread(o);
			thread.start();
			Thread thread2 = new Thread(o2);
			thread2.start();
			synchronized(pinger){
				pinger.wait(timeout);
			}
			r.setResult(s.string.trim());
		} catch (Exception e) {
			e.printStackTrace();
			System.err.println(res);
		}
		return r;
	}
	static class OutCounter {
		public int count=0;
	}
	static class OutListener implements Runnable {
		BufferedInputStream os;
		StringWrapper to;
		OutCounter c;
		public boolean run=true;
		public OutListener( BufferedInputStream os, StringWrapper to, OutCounter count ){
			this.os=os;
			this.to = to;
			c=count;
		}
		public void run() {
			
			while(run){
				try {
					int i = os.read();
					if(i == -1 ){
						c.count++;
						if( c.count>1 ){
							synchronized(pinger){
								pinger.notify();
							}
						}
						break;
					}
					to.string+=(char)i;
				} catch (IOException e) {
					break;
				}
			}
			try {
				os.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
	}
	/**
	 * @return the timeout
	 */
	public int getTimeout() {
		return timeout;
	}

	/**
	 * @param timeout the timeout to set
	 */
	public void setTimeout(int timeout) {
		this.timeout = timeout;
	}
	
	@org.junit.Test
	public void testMultiply() {
		System.out.println("== Multiply tests ==");
		
		runTest("edu/winona/csclub/testing/testCode/multiplyComprehensive.xml", new StupidCaseEnumProvider());
		runTest("edu/winona/csclub/testing/testCode/multiplyComprehensive.xml", new BoundaryValue(false));
		runTest("edu/winona/csclub/testing/testCode/multiplyComprehensive.xml", new BoundaryValue(true));
		runTest("edu/winona/csclub/testing/testCode/multiplyComprehensive.xml", new WorstCase(false));
		runTest("edu/winona/csclub/testing/testCode/multiplyComprehensive.xml", new WorstCase(true));
		
		System.out.println("=====================");
	}
	
	@org.junit.Test
	public void testTriangle() {
		System.out.println("== Triangle tests ==");
		
		runTest("edu/winona/csclub/testing/testCode/Triangle.xml", new StupidCaseEnumProvider());
		runTest("edu/winona/csclub/testing/testCode/Triangle.xml", new BoundaryValue(false));
		runTest("edu/winona/csclub/testing/testCode/Triangle.xml", new BoundaryValue(true));
		runTest("edu/winona/csclub/testing/testCode/Triangle.xml", new WorstCase(false));
		runTest("edu/winona/csclub/testing/testCode/Triangle.xml", new WorstCase(true));
		
		System.out.println("=====================");
	}
	
	@org.junit.Test
	public void testNextDate() {
		System.out.println("== NextDate tests ==");
		
		runTest("edu/winona/csclub/testing/testCode/calendar/NextDate.xml", new StupidCaseEnumProvider());
		runTest("edu/winona/csclub/testing/testCode/calendar/NextDate.xml", new BoundaryValue(false));
		runTest("edu/winona/csclub/testing/testCode/calendar/NextDate.xml", new BoundaryValue(true));
		runTest("edu/winona/csclub/testing/testCode/calendar/NextDate.xml", new WorstCase(false));
		runTest("edu/winona/csclub/testing/testCode/calendar/NextDate.xml", new WorstCase(true));
		
		System.out.println("=====================");
	}

	@org.junit.Test
	public void testCommission() {
		System.out.println("== Commission tests ==");
		
		runTest("edu/winona/csclub/testing/testCode/Commission.xml", new StupidCaseEnumProvider());
		runTest("edu/winona/csclub/testing/testCode/Commission.xml", new BoundaryValue(false));
		runTest("edu/winona/csclub/testing/testCode/Commission.xml", new BoundaryValue(true));
		runTest("edu/winona/csclub/testing/testCode/Commission.xml", new WorstCase(false));
		runTest("edu/winona/csclub/testing/testCode/Commission.xml", new WorstCase(true));
		
		System.out.println("=====================");
	}
	
	/**
	 *  Runs a test using the supplied config file and EnumProvider,
	 * outputting the results to StdOut
	 */
	private void runTest(String configFile, EnumProvider enumProvider) {
		System.out.println("Using " + enumProvider.getClass().getSimpleName() + ":");
		
		try {
			// generate the test cases using the EnumProvider and add them to a new TestSuite
			TestCase res = TestCaseGenerator.GenerateEnumTestCases(new ConfigFile(new File(configFile)), enumProvider);
			List<TestCase> cases= new LinkedList<TestCase>();
			cases.add(res);
			TestSuite ts = new TestSuite(cases);
			
			// run the test suite
			TestHarness th = new TestHarness(ts,10000);
			List<Result> results = th.execute();
			
			// output the results
			for(Result r : results)
				System.out.println(r);
		} catch(Exception e) {
			e.printStackTrace();
			Assert.fail(e.getMessage());
		}	
	}
}